
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>

typedef char ***base_reg_t;
typedef struct cthread cthread_t;
struct cthread
{
    jmp_buf jb;
    size_t stacksize;
    void (*entry)(void *arg);
    void *arg;
};

cthread_t tha, thb;

void cthread_init( cthread_t *p_this, size_t stacksize, void (*entry)(void*), void *arg )
{
    memset(p_this, 0x00, sizeof(*p_this));
    p_this->stacksize = stacksize;
    p_this->entry = entry;
    p_this->arg = arg;
}

cthread_t *g_ptr_current;

cthread_t *cthread_get_ready()
{
    if (g_ptr_current == 0){
        g_ptr_current = &tha;
        return &tha;
    }
    if (g_ptr_current == &tha ){
        g_ptr_current = &thb;
    } else {
        g_ptr_current = &tha;
    }

    return g_ptr_current;
}

void cthread_stack_extend( cthread_t *p_this, int index, base_reg_t *parent);

jmp_buf g_create_thread;

void cthread_wait_next_thread( void )
{
    cthread_t *p_thread = (cthread_t*)setjmp(g_create_thread);
    if (p_thread){
        cthread_stack_extend( p_thread, 0, 0 );
        while (1){
            ;/* never return here */
        }
    }
    if (g_ptr_current == 0){
        g_ptr_current = &tha;
        cthread_stack_extend( &thb, 0, 0 );
    }
    p_thread = cthread_get_ready();
    longjmp( p_thread->jb, 1 );
}

void cthread_stack_extend( cthread_t *p_this, int index, base_reg_t *parent)
{
    base_reg_t ptr[32] = { 0 };
    int stack_size = 0;

    if (index == 0){
        int ret = setjmp( p_this->jb );
        if (ret){
            p_this->entry( p_this->arg );
            p_this = cthread_get_ready();
            longjmp( p_this->jb, 1);
        }
        cthread_stack_extend(p_this, index+1, ptr );
    }
    else {
        stack_size = parent - (base_reg_t*)&ptr;
        if (stack_size < 0){
            stack_size = -stack_size;
        }
        stack_size *= sizeof(base_reg_t);
        if (stack_size >= p_this->stacksize ){
            cthread_wait_next_thread();
        }
        cthread_stack_extend(p_this, index + 1, parent);
    }
}


void entrya(void *a)
{
    int i=0;
    while (1){
        printf("thread a i=%d\n", i--);
        int a = setjmp(tha.jb);
        if (a == 0) {
            longjmp(thb.jb, &thb);
        }
    }
}

void entryb(void *a)
{
    int i=0;
    while (1){
        printf("thread b i=%d\n", i++);
        int a = setjmp(thb.jb);
        if (a == 0) {
            longjmp(tha.jb, &tha);
        }
    }
}
int main()
{
    printf("sizeof(char*)=%d\n", sizeof(char*));

    cthread_init(&tha, 8096*2, entrya, 0);
    cthread_init(&thb, 8096*2, entryb, 0);

    cthread_stack_extend( &tha, 0, 0 );

    return 0;
}

